/*
 * busconsole.c
 *
 *  Created on: Feb 17, 2011
 *      Author: s126135
 */

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <pthread.h>

#include "mod_general.h"
#include "../terminals.h"
#include "../list.h"

#define PRESENTATION "\e[1;34mHello. This is casperd daemon.\n\e[1;34mType help for list of available commands.\e[0m"

int sock = -1;

list* socks;

void* casper_console(void* arg) {
	terminal_t* term = (terminal_t*) arg;
	fd_set fds;
	int res, conn_sock, max;
	char buffer[4096];
	char* string;
	struct sockaddr_un my_addr, peer_addr;
	socklen_t peer_addr_size;

	buffer[4095] = 0;

	while (ioctl(term->pins.terminal_read, FIONREAD, (char*)&res) != -1 && res > 0)
		read(term->pins.terminal_read, buffer, 4095);

	terminal_write_to_daemon(term, "variables get CASPER_BUSCONSOLE_SOCKET");
	string = terminal_read_from_daemon(term);

	memset(&my_addr, 0, sizeof(struct sockaddr_un));

	my_addr.sun_family = AF_UNIX;
	strncpy(my_addr.sun_path, string, sizeof(my_addr.sun_path) - 1);

	fprintf(stderr, "INFO: busconsole: trying to open socket %s\n", string);
	if (bind(sock, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) {
		fprintf(stderr, "ERROR: busconsole: bind() failed: unable to bind local UNIX socket (errno: %d)\n", errno);
		return NULL;
	}

	listen(sock, 50);

	fprintf(stderr, "INFO: busconsole: unix local socket created: %s\n", string);

	free(string);

	peer_addr_size = sizeof(struct sockaddr_un);

	while (1) {
		max = term->pins.terminal_read;

		FD_ZERO(&fds);
		FD_SET(sock, &fds);
		FD_SET(term->pins.terminal_read, &fds);

		if (sock > max)
			max = sock;

		res = list_size(socks) - 1;
		for (; res >= 0; res--) {
			conn_sock = (int) list_get(socks, res);
			FD_SET(conn_sock, &fds);
			if (conn_sock > max)
				max = conn_sock;
		}
		res = select (max + 1, &fds, NULL, NULL, NULL);
		if (res == -1) {
			fprintf(stderr, "ERROR: busconsole: select() failed: exiting\n");
			return NULL;
		}

		// Accepting new connections
		if (FD_ISSET(sock, &fds)) {
			conn_sock = accept(sock, (struct sockaddr *) &peer_addr, &peer_addr_size);
			if (conn_sock <= 0)
				continue;

			fprintf(stderr, "INFO: busconsole: client connected\n");

			if (write(conn_sock, PRESENTATION, strlen(PRESENTATION)) <= 0)
				continue;

			list_add(socks, (void*) conn_sock, conn_sock);
		}
		// Data from daemon to clients
		if (FD_ISSET(term->pins.terminal_read, &fds)) {
			res = read(term->pins.terminal_read, buffer, 4095);
			if (res == 0 || res == -1) {
				fprintf(stderr, "INFO: busconsole: read() read nothing: exiting\n");
				return NULL;
			}
			max = list_size(socks) - 1;
			for (; max >= 0; max--) {
				conn_sock = (int) list_get(socks, max);
				if (write(conn_sock, buffer, res) <= 0) {
					//fprintf(stderr, "INFO: busconsole: write() wrote nothing: client disconnected\n");
					//list_remove_key(socks, conn_sock);
				}
			}
		}
		// Data from clients to daemon
		for (max = list_size(socks) - 1; max >= 0; max--) {
			conn_sock = (int) list_get(socks, max);
			if (!FD_ISSET(conn_sock, &fds))
				continue;
			res = read(conn_sock, buffer, 4095);
			if (res <= 0) {
				fprintf(stderr, "INFO: busconsole: read() read nothing: client disconnected\n");
				list_remove_key(socks, conn_sock);
				continue;
			}
			res = write(term->pins.terminal_write, buffer, res);
			if (res <= 0) {
				fprintf(stderr, "INFO: busconsole: write() wrote nothing: exiting\n");
				return NULL;
			}
		}

	}

}

int module_init(terminal_t* term) {
	pthread_t tid;
	int res;

	socks = list_new();

	if (sock == -1) {
		sock = socket(AF_UNIX, SOCK_STREAM, 0);
		if (sock == -1) {
			fprintf(stderr, "ERROR: busconsole: socket() failed: unable to create local UNIX socket (errno: %d)\n", errno);
			return errno;
		}
	}

	res = pthread_create(&tid, NULL, casper_console, term);
	if (res != 0) {
		fprintf(stderr, "ERROR: busconsole: unable to create thread (pthread_create: %d)\n", res);
		return errno;
	}
	fprintf(stderr, "INFO: busconsole: created console server thread\n");

	return 0;
}
